import re
import pywintypes

from ..win_control.control import Control
from ..exceptions import CsClientError


class WebElement(Control):
    '''
    网页元素操作
    '''

    def parent(self):
        '''
        获取网页元素的父级元素

        :Returns:
            返回网页元素的父级元素，该网页元素为根节点，则返回None
        '''
        webelement = self.control.Parent
        return WebElement(webelement)

    def get_tag_name(self):
        '''
        获取网页元素的tagname

        :Returns:
            返回网页元素的tagname
        '''
        return self.control.TagName

    def get_inner_text(self):
        '''
        获取网页元素的innertext

        :Returns:
            返回网页元素的innertext
        '''
        return self.control.GetInnerText

    def set_inner_text(self, txt):
        '''
        设置网页元素的innertext

        :Args:
         - txt: innertext内容
        '''
        self.control.SetInnerText(txt)

    def get_id(self):
        '''
        获取网页元素的ID

        :Returns:
            返回网页元素的ID
        '''
        return self.control.GetId

    def set_id(self, id):
        '''
        设置网页元素的ID

        :Args:
         - id: 网页元素的ID
        '''
        self.control.SetId(id)

    def get_hidden(self):
        '''
        获取网页元素是否隐藏

        :Returns:
            返回网页元素是否隐藏
        '''
        return self.control.GetHidden

    def set_hidden(self, hidden):
        '''
        设置网页元素隐藏属性

        :Args:
         - hidden: 网页元素隐藏属性
        '''
        self.control.SetHidden(hidden)

    def get_draggable(self):
        '''
        获取网页元素是否允许拖拽

        :Returns:
            返回网页元素是否允许拖拽
        '''
        return self.control.GetDraggable

    def set_draggable(self, draggable):
        '''
        设置网页元素是否允许拖拽

        :Args:
         - draggable: 网页元素是否允许拖拽属性
        '''
        self.control.SetDraggable(draggable)

    def get_context_menu(self):
        '''
        获取网页元素的右键菜单内容

        :Returns:
            返回网页元素的右键菜单内容
        '''
        return self.control.GetContextMenu

    def set_context_menu(self, context_menu):
        '''
        设置网页元素的右键菜单内容

        :Args:
         - context_menu: 网页元素的右键菜单内容
        '''
        self.control.SetContextMenu(context_menu)

    def get_content_editable(self):
        '''
        获取网页元素是否能编辑内容

        :Returns:
            返回网页元素是否能编辑内容
        '''
        return self.control.GetContentEditable

    def set_content_editable(self, content_editable):
        '''
        设置网页元素是否能编辑内容

        :Args:
         - content_editable: 网页元素是否能编辑内容属性
        '''
        self.control.SetContentEditable(content_editable)

    def get_css_class(self):
        '''
        获取网页元素的css class属性

        :Returns:
            返回网页元素的css class属性值
        '''
        return self.control.GetCssClass

    def set_css_class(self, css_class):
        '''
        设置网页元素的css class属性

        :Args:
         - css_class: 网页元素的css class属性
        '''
        self.control.SetCssClass(css_class)

    def get_tag_value(self):
        '''
        获取网页元素的tag value

        :Returns:
            返回网页元素的tag value
        '''
        return self.control.GetTagValue

    def set_tag_value(self, tag_value):
        '''
        设置网页元素的tag value

        :Args:
         - tag_value: 网页元素的tag value
        '''
        self.control.SetTagValue(tag_value)

    def next_sibling(self):
        '''
        获取下一个同级网页元素

        :Returns:
            返回下一个同级网页元素，如果此元素其最后一个元素，则为None
        '''
        webelement = self.control.NextSibling
        return WebElement(webelement)

    def previous_sibling(self):
        '''
        获取上一个同级网页元素

        :Returns:
            返回上一个同级网页元素，如果此元素是第一个元素，则为None
        '''
        webelement = self.control.PreviousSibling
        return WebElement(webelement)

    def get_inner_html(self):
        '''
        获取网页元素的inner html

        :Returns:
            返回网页元素的inner html
        '''
        return self.control.GetInnerHtml

    def get_or_create_id(self):
        '''
        获取网页元素的唯一ID属性。如果设置了ID，则返回原始值，否则网页元素将被分配一个新的随机ID。

        :Returns:
            返回网页元素的唯一ID属性
        '''
        return self.control.GetOrCreateId

    def get_style(self, name):
        '''
        根据style的名称获取style值

        :Args:
         - name: style的名称

        :Returns:
            返回style值
        '''
        return self.control.GetStyle(name)

    def set_style(self, name, value):
        '''
        根据style名称设置style值

        :Args:
         - name: style名称
         - value: style值
        '''
        self.control.SetStyle(name, value)

    def perform_click(self):
        '''
        不适用鼠标点击网页元素
        '''
        self.control.PerformClick

    def raise_event(self, name):
        '''
        根据事件名在网页元素上引发事件

        :Args:
         - name: 事件名
        '''
        self.control.RaiseEvent(name)

    def owner_document(self):
        '''
        获取webelement所在的webdocument

        :Returns:
            返回webelement所在的webdocument，如果不存在则返回None
        '''
        return Control(self.control.OwnerDocument)

    # 对应selenium的方法
    def get_attribute(self, name):
        '''
        获取属性值

        :Args:
         - name: 属性名称

        :Returns:
            返回属性值
        '''
        if name == 'class':
            return self.get_css_class()
        elif name == 'id':
            return self.get_id()
        else:
            return self.get_attribute_value(name)

    @property
    def tag_name(self):
        '''
        获取网页元素的tagname

        :Returns:
            返回网页元素的tagname
        '''
        return self.get_tag_name()

    @property
    def text(self):
        return self.get_inner_text()

    def is_readonly(self):
        """
        判断元素是否是只读状态

        :Returns:
            只读则返回True，非只读则返回False

        :Example:
         - 例: element.is_readonly()
        """
        result = self.get_attribute_value('readonly')
        if result is None:
            return False
        else:
            return True

    def secure_click(self):
        '''
        元素点击，点击元素的中心点
        '''
        self.click()

    def left_click(self):
        '''
        元素点击，点击元素的中心点
        '''
        self.click()

    def is_displayed(self):
        '''
        元素是否可见

        :Returns:
            True表示可见，反之表示不可见
        '''
        return self.is_visible()

    def is_enabled(self):
        '''
        元素是否使能

        :Returns:
            True表示使能，反之表示不使能
        '''
        return self.is_enable()

    @property
    def location(self):
        '''
        获取元素位置

        :Returns:
            返回元素位置(x,y)
        '''
        return (self.get_control_screen_location_x(), self.get_control_screen_location_y())

    @property
    def size(self):
        '''
        获取元素大小

        :Returns:
            返回元素大小(height,width)
        '''
        return (self.get_control_height(), self.get_control_width())

    def clear(self):
        '''
        清空
        '''
        self.send_keys('')

    def find_element_by_attribute(self, by, value, assist='', timeout=10000):
        '''
        根据多属性查找当前元素节点下符合查找条件的元素集合，assist指的是辅助属性

        :Args:
         - by: 必须是'xpath'
         - value: ranorexpath
         - assist: 辅助属性，'属性名称:属性值'，支持多组属性，多组之间用逗号隔开
         - timeout: 查找元素的超时时间，在timeout时间内会不断的查找直到找到为止，超过timeout时间则不再继续查找

        :Returns:
            返回符合条件的元素，WebElement类型

        :Raises:
            CsClientError: 参数不正确

        :Example:
         - 例: ele_list = element.find_elements_by_attribute('xpath', '//input[@id="su"]', 'text:百度一下', 3)
        '''
        # if by.upper() == 'ID':
        #     value = '//*[#"{}"]'.format(value)
        # elif by.upper() == 'NAME':
        #     value = '//*[@name=\'{}\']'.format(value)
        # elif by.upper() == 'TAG NAME':
        #     value = '//{}'.format(value)
        # elif by.upper() == 'CLASS NAME':
        #     value = '//*[@class=\'{0}\' or @class~\'^{0} \' or @class~\' {0}$\' or @class~\' {0} \']'.format(
        #         value)
        # elif by.upper() == 'LINK TEXT':
        #     value = '//a[@text=\'{}\']'.format(value)
        # elif by.upper() == 'PARTIAL LINK TEXT':
        #     value = '//a[@text~\'{}\']'.format(value)
        # elif by.upper() == 'CSS SELECTOR':
        #     value = './/' + value
        #     # 替换分层符
        #     value = value.replace(' > ', '/')
        #     value = value.replace('>', '/')
        #     value = value.replace(' ', '//')
        #     # 替换后同级节点
        #     value = value.replace('~', '/following-sibling::')
        #     # 替换id属性
        #     id_list = re.findall(r'#([\w\-]+)', value)
        #     for id in id_list:
        #         value = re.sub(r'#' + id, '[#\'{}\']'.format(id), value)
        #     # 替换class属性
        #     class_list = re.findall(r'\.([\w\-]+)', value)
        #     for cl in class_list:
        #         value = re.sub(r'\.' + cl,
        #             '[@class=\'{0}\' or @class~\'^{0} \' or @class~\' {0}$\' or @class~\' {0} \']'.format(cl),
        #                        value, 1)
        #     # 替换其他属性
        #     type_list = re.findall(r'\[(\w+)=', value)
        #     for ty in type_list:
        #         value = value.replace(ty, '@' + ty, 1)
        #     # 连接多属性约束条件
        #     value = value.replace('][', ' and ')
        #     # 替换序号
        #     value = value.replace(':last-child', '[-1]')
        #     value = value.replace(':first-child', '[1]')
        #     child_list = re.findall(r':nth-child\((\d+)\)', value)
        #     for child in child_list:
        #         value = value.replace(':nth-child({})'.format(child), '[{}]'.format(child), 1)
        #     # 替换兄弟节点
        #     follow_list = re.findall(r'\+(\w+)', value)
        #     for follow in follow_list:
        #         value = re.sub(r'\+' + follow, '/following-sibling::{}[1]'.format(follow), value)
        #     print(value)
        # elif by.upper() == 'XPATH':
        #     pass
        # else:
        if by.upper() != 'XPATH' and by.upper() != 'RANOREXPATH':
            raise CsClientError('Ranorex只通过RanoreXpath访问元素！')
        if not assist:
            return self.find_element(value, timeout)
        else:
            element_list = self.find_elements_by_attribute(by, value, assist, timeout)
            if element_list:
                return element_list[0]
            else:
                raise CsClientError(by + ',' + value + ',' + assist + '属性对象没有找到!')

    def find_elements_by_attribute(self, by, value, assist='', timeout=10000):
        '''
        根据多属性查找当前元素节点下符合查找条件的元素集合，assist指的是辅助属性

        :Args:
         - by: 必须是'xpath'
         - value: ranorexpath
         - assist: 辅助属性，'属性名称:属性值'，支持多组属性，多组之间用逗号隔开
         - timeout: 查找元素的超时时间，在timeout时间内会不断的查找直到找到为止，超过timeout时间则不再继续查找

        :Returns:
            返回符合条件的元素集合，[WebElement]类型

        :Raises:
            CsClientError: 参数不正确

        :Example:
         - 例: ele_list = element.find_elements_by_attribute('xpath', '//input[@id="su"]', 'text:百度一下', 3)
        '''
        # if by.upper() == 'ID':
        #     value = '//*[#\'{}\']'.format(value)
        # elif by.upper() == 'NAME':
        #     value = '//*[@name=\'{}\']'.format(value)
        # elif by.upper() == 'TAG NAME':
        #     value = '//{}'.format(value)
        # elif by.upper() == 'CLASS NAME':
        #     value = '//*[@class=\'{0}\' or @class~\'^{0} \' or @class~\' {0}$\' or @class~\' {0} \']'.format(
        #         value)
        # elif by.upper() == 'LINK TEXT':
        #     value = '//a[text()=\'{}\']'.format(value)
        # elif by.upper() == 'PARTIAL LINK TEXT':
        #     value = '//a[contains(text(), \'{}\')]'.format(value)
        # elif by.upper() == 'CSS SELECTOR':
        #     value = './/' + value
        #     # 替换分层符
        #     value = value.replace(' > ', '/')
        #     value = value.replace('>', '/')
        #     value = value.replace(' ', '//')
        #     # 替换兄弟节点
        #     value = value.replace('~', '/following-sibling::')
        #     # 替换id属性
        #     id_list = re.findall(r'#([\w\-]+)', value)
        #     for id in id_list:
        #         value = re.sub(r'#' + id, '[#\'{}\']'.format(id), value)
        #     # 替换class属性
        #     class_list = re.findall(r'\.([\w\-]+)', value)
        #     for cl in class_list:
        #         value = re.sub(r'\.' + cl,
        #             '[@class=\'{0}\' or @class~\'^{0} \' or @class~\' {0}$\' or @class~\' {0} \']'.format(cl),
        #                        value, 1)
        #     # 替换其他属性
        #     type_list = re.findall(r'\[(\w+)=', value)
        #     for ty in type_list:
        #         value = value.replace(ty, '@' + ty, 1)
        #     # 连接多属性约束条件
        #     value = value.replace('][', ' and ')
        #     # 替换序号
        #     value = value.replace(':last-child', '[-1]')
        #     value = value.replace(':first-child', '[1]')
        #     child_list = re.findall(r':nth-child\((\d+)\)', value)
        #     for child in child_list:
        #         value = value.replace(':nth-child({})'.format(child), '[{}]'.format(child), 1)
        #     # 替换兄弟节点
        #     follow_list = re.findall(r'\+(\w+)', value)
        #     for follow in follow_list:
        #         value = re.sub(r'\+' + follow, '/following-sibling::{}[1]'.format(follow), value)
        #     print(value)
        # elif by.upper() == 'XPATH':
        #     pass
        # else:
        if by.upper() != 'XPATH' and by.upper() != 'RANOREXPATH':
            raise CsClientError('Ranorex只通过RanoreXpath访问元素！')
        if not assist:
            return self.find_elements(value, timeout)
        else:
            element_list = self.find_elements(value, timeout)
            if len(element_list) > 0:
                assist_list = assist.split(',')
                i = 0
                while i < len(element_list):
                    for e in assist_list:
                        index_number = e.index(':')
                        if e[0:index_number] == 'text':
                            temp = e[index_number + 1:]
                            # 因为用正则表达式进行文本匹配，文本可以含有正则表达式的特殊字符，所以把特殊字符进行转义
                            re_special_character = {'.': '\.'
                                                    , '*': '\*'
                                                    , '(': '\('
                                                    , ')': '\)'
                                                    , '$': '\$'
                                                    , '+': '\+'
                                                    , '[': '\['
                                                    , ']': '\]'
                                                    , '?': '\?'
                                                    , '{': '\{'
                                                    , '|': '\|'}
                            for key, value in re_special_character.items():
                                temp = temp.replace(key, value)
                            re_str = '^' + temp + '$'
                            if e[index_number + 1:].startswith('.'):
                                re_str = re_str.strip('^')
                            if e[index_number + 1:].endswith('.*'):
                                re_str = re_str.strip('$')
                            e_html_text = element_list[i].get_inner_html()
                            e_text = re.sub('<[^<>]*>', '', e_html_text).strip()
                            if not e_text or not re.search(re_str, e_text):
                                element_list.remove(element_list[i])
                            else:
                                i += 1
                        elif e[0:index_number] == 'style':
                            re_str = e[index_number + 1:]
                            index_no = re_str.index(':')
                            element_style = element_list[i].get_style(re_str[0:index_no])
                            re_word = re_str[index_no + 1:].strip(';').strip()
                            if element_style == re_word:
                                i += 1
                            else:
                                element_list.remove(element_list[i])
                        else:
                            # 正则表达式中需要做设定，必须以给定的字符串打头和结尾
                            element_attr = element_list[i].get_attribute(e[0:index_number])
                            re_word = e[index_number + 1:]
                            if re_word not in element_attr.split(' '):
                                element_list.remove(element_list[i])
                            else:
                                i += 1
                return element_list
            else:
                return element_list

    def exist(self, by, value, assist='', timeout=10000):
        '''
        根据多属性查找当前元素节点下符合查找条件的元素集合，assist指的是辅助属性

        :Args:
         - by: 必须是'xpath'
         - value: ranorexpath
         - assist: 辅助属性，'属性名称:属性值'，支持多组属性，多组之间用逗号隔开
         - timeout: 查找元素的超时时间，在timeout时间内会不断的查找直到找到为止，超过timeout时间则不再继续查找

        :Returns:
            存在返回符合条件的元素，不存在返回None

        :Raises:
            CsClientError: 参数不正确

        :Example:
         - 例: ele = element.exist('xpath', '//input[@id="su"]', 'text:百度一下', 3)
        '''
        if by.upper() != 'XPATH':
            raise CsClientError('Ranorex只通过RanoreXpath访问元素！')
        try:
            if not assist:
                if self.is_element_exist(value, timeout):
                    return self.find_element(value, timeout)
                else:
                    return None
            else:
                element = self.find_element_by_attribute(by, value, assist, timeout)
                return element
        except CsClientError:
            return None

    def find_element(self, xpath, time=10000):
        '''
        根据相对路径查找元素，如果有多个控件符合，只返回第一个找到的控件

        :Args:
         - xpath: 相对此控件的Ranorexpath
         - time: 查找时间，默认为10S，单位ms

        :Returns:
            返回控件的对象
        '''
        try:
            webelement = self.control.FindElement(xpath, time)
            return WebElement(webelement)
        except pywintypes.com_error as msg:
            raise CsClientError(xpath + '对象查找出错：' + str(msg))

    def find_elements(self, xpath, time=10000):
        '''
        根据相对路径查找所有符合此Path的所有元素

        :Args:
         - xpath: 相对此控件的Ranorexpath
         - time: 查找时间，默认为10S，单位ms

        :Returns:
            返回控件的对象集合
        '''
        webelement_list = self.control.FindElements(xpath, time)
        return [WebElement(control) for control in webelement_list]
